package org.example.infrastructure.persistent.repository;


import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.example.domain.activity.model.valobj.UserRaffleOrderStateVO;
import org.example.domain.award.model.aggregate.GiveOutPrizesAggregate;
import org.example.domain.award.model.aggregate.UserAwardRecordAggregate;
import org.example.domain.award.model.entity.AwardEntity;
import org.example.domain.task.model.entity.TaskEntity;
import org.example.domain.award.model.entity.UserAwardRecordEntity;
import org.example.domain.award.model.entity.UserCreditAwardEntity;
import org.example.domain.award.model.valobj.AccountStatusVO;
import org.example.domain.award.repository.IAwardRepository;
import org.example.infrastructure.event.EventPublisher;
import org.example.infrastructure.persistent.dao.*;
import org.example.infrastructure.persistent.po.Task;
import org.example.infrastructure.persistent.po.UserAwardRecord;
import org.example.infrastructure.persistent.po.UserCreditAccount;
import org.example.infrastructure.persistent.po.UserRaffleOrder;
import org.example.types.enums.ResponseCode;
import org.example.types.exception.AppException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

@Slf4j
@Repository
public class AwardRepository implements IAwardRepository {

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private IDBRouterStrategy dbRouter;

    @Resource
    private IUserAwardRecordDao userAwardRecordDao;

    @Resource
    private ITaskDao taskDao;

    @Resource
    private IUserRaffleOrderDao userRaffleOrderDao;

    @Resource
    private EventPublisher eventPublisher;

    @Resource
    private IUserCreditAccountDao userCreditAccountDao;

    @Resource
    private IAwardDao awardDao;

    /**
     * 保存中奖记录聚合对象
     * 1.保存中奖记录 ,保存消息流水, 发送消息,
     * 2.更新抽奖单状态
     * @param userAwardRecordAggregate 聚合对象
     */
    @Override
    public void saveUserAwardRecord(UserAwardRecordAggregate userAwardRecordAggregate) {

        TaskEntity  taskEntity= userAwardRecordAggregate.getTaskEntity();
        UserAwardRecordEntity userAwardRecordEntity = userAwardRecordAggregate.getUserAwardRecordEntity();
        String userId = userAwardRecordEntity.getUserId();

        Task task = Task.builder()
                .userId(taskEntity.getUserId())
                .topic(taskEntity.getTopic())
                .messageId(taskEntity.getMessageId())
                .message(taskEntity.getMessage())
                .state(taskEntity.getState().getCode())
                .build();

        UserAwardRecord userAwardRecord = UserAwardRecord.builder()
                .userId(userAwardRecordEntity.getUserId())
                .activityId(userAwardRecordEntity.getActivityId())
                .strategyId(userAwardRecordEntity.getStrategyId())
                .orderId(userAwardRecordEntity.getOrderId())
                .awardId(userAwardRecordEntity.getAwardId())
                .awardTitle(userAwardRecordEntity.getAwardTitle())
                .awardTime(userAwardRecordEntity.getAwardTime())
                .awardState(userAwardRecordEntity.getAwardState().getCode())
                .build();

        UserRaffleOrder userRaffleOrderReq = UserRaffleOrder.builder()
                .orderId(userAwardRecordEntity.getOrderId())
                .userId(userId)
                .build();

        try {
            dbRouter.doRouter(userAwardRecordEntity.getUserId());
            transactionTemplate.execute(status -> {

                try {
                    userAwardRecordDao.insert(userAwardRecord);
                    taskDao.insert(task);
                    //更新抽奖单
                    int count = userRaffleOrderDao.updateUserRaffleOrderStateUsed(userRaffleOrderReq);
                    if(count != 1){
                        status.setRollbackOnly();
                        log.info("抽奖单已经抽过奖了,不可重复抽奖");
                        throw new AppException(ResponseCode.ACTIVITY_ORDER_ERROR.getCode(), ResponseCode.ACTIVITY_ORDER_ERROR.getInfo());
                    }
                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.info("记录插入失败");
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        }  finally {
            dbRouter.clear();
        }

        //发送消息,发送成功直接更新流水为完成,出错则更改为失败,让定时任务去重发
        try {
            // 发送消息【在事务外执行，如果失败还有任务补偿】
            eventPublisher.publish(task.getTopic(), task.getMessage());
            // 更新数据库记录，task 任务表
            taskDao.updateTaskSendMessageCompleted(task);
        } catch (Exception e) {
            log.error("写入中奖记录，发送MQ消息失败 userId: {} topic: {}", userId, task.getTopic());
            taskDao.updateTaskSendMessageFail(task);
        }


    }

    /**
     * 积分发奖入库
     * 直接增加用户积分账户数额(没有则新建),不用创建积分订单
     * 修改抽奖订单状态为已完成
     * @param giveOutPrizesAggregate
     */
    @Override
    public void saveGiveOutPrizesAggregate(GiveOutPrizesAggregate giveOutPrizesAggregate) {
        String userId = giveOutPrizesAggregate.getUserId();
        UserCreditAwardEntity userCreditAwardEntity = giveOutPrizesAggregate.getUserCreditAwardEntity();
        UserCreditAccount userCreditAccount = UserCreditAccount.builder()
                .availableAmount(userCreditAwardEntity.getCreditAmount())
                .totalAmount(userCreditAwardEntity.getCreditAmount())
                .accountStatus(AccountStatusVO.open.getCode())
                .userId(userId)
                .build();
        UserAwardRecordEntity userAwardRecordEntity = giveOutPrizesAggregate.getUserAwardRecordEntity();
        UserAwardRecord userAwardRecord= UserAwardRecord.builder()
                .userId(userId)
                .orderId(userAwardRecordEntity.getOrderId())
                .awardId(userAwardRecordEntity.getAwardId())
                .build();


        dbRouter.doRouter(userId);
        transactionTemplate.execute(status -> {
            try {
                int updateAccountCount = userCreditAccountDao.updateAddAmount(userCreditAccount);
                if (updateAccountCount != 1){
                    log.info("没有该用户的积分账户,创建一个");
                    userCreditAccountDao.insert(userCreditAccount);
                }
                int updateAwardCount = userAwardRecordDao.updateRecordStateCompleted(userAwardRecord);
                if (updateAwardCount == 0){
                    log.warn("更新中奖记录，重复更新拦截 userId:{} giveOutPrizesAggregate:{}", userId, JSON.toJSONString(giveOutPrizesAggregate));
                    status.setRollbackOnly();
                }
                return 1;
            } catch (DuplicateKeyException e) {
                status.setRollbackOnly();
                log.error("更新中奖记录，唯一索引冲突 userId: {} ", userId, e);
                throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
            } finally {
                dbRouter.clear();

            }
        });


    }

    @Override
    public String queryAwardKeyByAwardId(Integer awardId) {
        String awardKey = awardDao.queryAwardKey(awardId);


        return awardKey;
    }

    @Override
    public String queryAwardConfigByAwardId(Integer awardId) {
        return awardDao.queryAwardConfigByAwardId(awardId);
    }

}
